// 套接字中于TCP连接池相关的函数在这里定义
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>// usleep函数

#include "my_memory.h"
#include "global.h"
#include "my_lock.h"
#include "log.h"
#include "net/comm.h"
#include "net/socket.h"
#include "net/netglobal.h"


// TCP连接结构体构造函数
connection_s::connection_s(){
    iCurrsequence = 0;
    pthread_mutex_init(&logicPorcMutex, NULL); // 互斥量初始化
}

// TCP连接结构体析构函数
connection_s::~connection_s(){
    pthread_mutex_destroy(&logicPorcMutex); // 互斥量释放
}

// 分配一个TCP连接前的自初始化函数
void connection_s::GetOneToUse(){
    ++iCurrsequence;
    // 初始化一些值
    fd = -1;
    curStat = _PKG_HD_INIT;// comm中定义的初始状态，值为0
    precvbuf = dataHeadInfo;// 由于先收包头，此时数据包缓冲区里的就是包头信息
    irecvlen = sizeof(COMM_PKG_HEADER);// 包结构体的大小

    precvMemPointer = NULL;// 收包此时还没分配内存，先置空
    iThrowsendCount = 0;// 原子操作置0，表示发送缓冲区空闲
    psendMemPointer = NULL;// 发包此时还没分配内存，先置空
    events = 0;// epoll监听类型先给0
    lastPingTime = time(NULL);// 上次ping的时间是现在

    FloodkickLastTime = 0;// 上次收到攻击包的时间置0
    FloodAttackCount = 0;// 攻击次数置0
    iSendCount = 0;// 发送队列中有的数据条目数置0
}

// 回收一个TCP连接前的自处理函数
void connection_s::PutOneToFree(){
    ++iCurrsequence;// 回收也要自增一次
    if(precvMemPointer != NULL){
        CMemory::GetInstance()->FreeMemory(precvMemPointer);
        precvMemPointer = NULL;
    }
    if(psendMemPointer != NULL){
        CMemory::GetInstance()->FreeMemory(psendMemPointer);
        psendMemPointer = NULL;
    }

    iThrowsendCount = 0;// 其实可以不设置
}

// 一些CSocket类中的TCP连接池相关的函数定义
void CSocket::Initconnection(){
    lpconnection_t p_Conn;// 保存新建连接池指针
    CMemory *p_memory = CMemory::GetInstance();

    int ilenconnpool = sizeof(connection_t);// 单个连接的长度
    for(int i = 0; i < m_worker_connections; ++i){// 数量就是epoll监听的最大个数
        p_Conn = (lpconnection_t)p_memory->AllocMemory(ilenconnpool, true);// 分配原始内存（不会调用构造函数）
        p_Conn = new(p_Conn) connection_t();// 在原始内存上构造对象（会调用构造函数）
        p_Conn->GetOneToUse();// 进行初始化
        m_connectionList.push_back(p_Conn);// 放入容器（不管是不是空闲）
        m_freeconnectionList.push_back(p_Conn);// 放入空闲队列
    }
    m_free_connection_n = m_total_connection_n = m_connectionList.size();
}

// 最终回收TCP连接池
void CSocket::Clearconnection(){
    lpconnection_t p_Conn;
    CMemory *p_memory = CMemory::GetInstance();

    while (!m_connectionList.empty())
    {
        p_Conn = m_connectionList.front();
        m_connectionList.pop_front();
        p_Conn->~connection_s();
        p_memory->FreeMemory(p_Conn);
    }
}

// 从连接池中获取一个空闲连接（当一个客户端TCP连接进入，将这个连接和连接池中的一个连接绑定起来）
lpconnection_t CSocket::Get_connection(int fd){
    // 可能有其他线程同时访问容器，进行互斥
    CLock lock(&m_connectionMutex);

    if(!m_freeconnectionList.empty()){
        lpconnection_t p_Conn = m_freeconnectionList.front();
        m_freeconnectionList.pop_front();
        p_Conn->GetOneToUse();
        --m_free_connection_n;
        p_Conn->fd = fd;
        return p_Conn;
    }

    // 走到这里说明没有空闲的连接了，创建一个新的
    CMemory *p_memory = CMemory::GetInstance();
    lpconnection_t p_Conn = (lpconnection_t)p_memory->AllocMemory(sizeof(connection_t),true);
    p_Conn = new(p_Conn) connection_t();
    p_Conn->GetOneToUse();
    m_connectionList.push_back(p_Conn);// 放到总容器中
    ++m_total_connection_n;
    p_Conn->fd = fd;
    return p_Conn;
}

// 归还一个连接到连接池中
void CSocket::Free_connection(lpconnection_t pConn){
    CLock lock(&m_connectionMutex);
    pConn->PutOneToFree();
    m_freeconnectionList.push_back(pConn);// 放到空闲队列
    ++m_free_connection_n;// 空闲个数自增
}

// 归还一个连接到需要延迟回收的队列中，后续有专门的线程进程处理
// 有些连接隔一段时间再回收有助于服务器稳定
void CSocket::InRecyConnectQueue(lpconnection_t pConn){
    std::list<lpconnection_t>::iterator pos;
    bool iffind = false;
    CLock lock(&m_recyconnqueueMutex);// 锁定回收队列的互斥量

    // 增加判断防止多次被放入回收队列
    for(pos = m_recyconnectionList.begin(); pos != m_recyconnectionList.end(); ++pos){
        if((*pos) == pConn){
            iffind = true;
            break;
        }
    }
    if(iffind == true){
        return;// 已经在里面就不放入了
    }

    // 准备放入回收队列
    pConn->inRecyTime = time(NULL);
    ++pConn->iCurrsequence;// 分配序号自增
    m_recyconnectionList.push_back(pConn);
    ++m_totol_recyconnection_n;// 待释放队列大小自增
    --m_onlineUserCount;// 连入的用户数量自减
}

/**
 * 根据fd获取到客户端的一个连接池对象
 * @param fd 客户端的套接字
 * @return 返回对应的连接池对象
 */
lpconnection_t CSocket::Get_connectionByFd(int fd){
    // 由于只是读取，不需要加锁
    lpconnection_t tmp = nullptr;
    for(auto it = m_connectionList.begin(); it != m_connectionList.end(); ++it){
        if((*it)->fd == fd){
            tmp = *it;
            break;
        }
    }
    return tmp;
}

// 处理回收连接的线程
void* CSocket::ServerRecyConnectionThread(void *threadData){
    ThreadItem *pThread = static_cast<ThreadItem*>(threadData);
    CSocket *pSocketObj = pThread->_pThis;

    time_t currtime;
    int err;
    std::list<lpconnection_t>::iterator pos;
    lpconnection_t p_Conn;

    // 进行处理(一直循环)
    while(true){
        usleep(1000*1000);// 每次循环休息1秒（其实可以优化，改用定时器）
        
        // 如果待回收队列长度不为0，进行处理
        if(pSocketObj->m_totol_recyconnection_n > 0){
            currtime = time(NULL);
            err = pthread_mutex_lock(&pSocketObj->m_recyconnqueueMutex);
            if(err != 0){
                Log_Error(err,"CSocket::ServerRecyConnectionThread()中pthread_mutex_lock()失败，返回的错误码为%d!",err);
            }

            for(pos = pSocketObj->m_recyconnectionList.begin(); pos != pSocketObj->m_recyconnectionList.end(); ++pos){
                p_Conn = (*pos);
                // 判断是否到了该释放的时间并且系统没有关闭
                if(((p_Conn->inRecyTime + pSocketObj->m_RecyConnectionWaitTime) > currtime) && (g_stopEvent == 0)){
                    continue;// 还没到释放的时间
                }

                // TODO:是否允许释放逻辑待做

                // 判断此时的发送缓冲区是否为空（标志为0），理论上放进回收队列前就清空了
                if(p_Conn->iThrowsendCount > 0){
                    Log_Error(0,"CSocket::ServerRecyConnectionThread()中到释放时间却发现p_Conn.iThrowsendCount!=0，这个不该发生");
                }

                // 正式开始释放流程(软释放)
                --pSocketObj->m_totol_recyconnection_n;// 回收队列大小自减
                pos = pSocketObj->m_recyconnectionList.erase(pos);// 此时迭代器被更新到下一个有效位置
                if (pos != pSocketObj->m_recyconnectionList.begin()){
                    pos--;// 防止for循环的自增进行干扰
                } 
                pSocketObj->Free_connection(p_Conn);// 将连接放回连接池
            }

            err = pthread_mutex_unlock(&pSocketObj->m_recyconnqueueMutex);
            if(err != 0){
                Log_Error(err,"CSocket::ServerRecyConnectionThread()pthread_mutex_unlock()失败，返回的错误码为%d!",err);
            }
        }

        // 如果是要退出整个程序了，进行硬释放（不管到没到等待时间都释放）
        if(g_stopEvent == 1){
            err = pthread_mutex_lock(&pSocketObj->m_recyconnqueueMutex);  
            if(err != 0){
                Log_Error(err,"CSocket::ServerRecyConnectionThread()中pthread_mutex_lock2()失败，返回的错误码为%d!",err);
            }

            for(pos = pSocketObj->m_recyconnectionList.begin(); pos != pSocketObj->m_recyconnectionList.end(); ++pos){
                p_Conn = (*pos);
                --pSocketObj->m_totol_recyconnection_n;// 回收队列大小自减
                pos = pSocketObj->m_recyconnectionList.erase(pos);// 此时迭代器被更新到下一个有效位置
                if (pos != pSocketObj->m_recyconnectionList.begin()){
                    pos--;// 防止for循环的自增进行干扰
                }
                pSocketObj->Free_connection(p_Conn);// 将连接放回连接池
            }

            err = pthread_mutex_unlock(&pSocketObj->m_recyconnqueueMutex); 
            if(err != 0){
                Log_Error(err,"CSocket::ServerRecyConnectionThread()pthread_mutex_unlock2()失败，返回的错误码为%d!",err);
            }

            break;// 因为系统要关闭了，退出死循环
        }
    }

    return (void*)0;
}

// 连接关闭函数，用于资源释放
void CSocket::Close_connection(lpconnection_t pConn){
    Free_connection(pConn);
    if(pConn->fd != -1){
        close(pConn->fd);
        pConn->fd = -1;// 关闭套接字
    }
}